#include <fermat/dio/disk_queues.h>
#include <fermat/dio/file_base.h>
#include <fermat/dio/iostats.h>
#include <fermat/dio/request.h>
#include <fermat/dio/serving_request.h>

#include <ostream>

namespace fermat {

Request::Request(
    const CompletionHandler& on_complete,
    const FileBasePtr& pfile,
    void* pbuffer,
    offset_type poffset,
    size_type pbytes,
    ReadOrWriteType rwtype)
    : _on_complete(on_complete),
      _file(pfile),
      _buffer(pbuffer),
      _offset(poffset),
      _bytes(pbytes),
      _type(rwtype),
      _state(OP),
      _sucess(false)
{
        _saved_error = 0;
}

Request::~Request()
{
    
}

void Request::check_alignment() const 
{

}

void Request::check_nref_failed(bool after)
{
	(void)after;
}

const char* Request::io_type() const {
    return _file->io_type();
}

std::ostream& Request::print(std::ostream& out) const {
    out << "File object address: " << _file;
    out << " Buffer address: " << static_cast<void*>(_buffer);
    out << " File offset: " << _offset;
    out << " Transfer size: " << _bytes << " bytes";
    out << " Type of transfer: " << ((_type == READ) ? "READ" : "WRITE");
    return out;
}

std::ostream& operator << (std::ostream& out, const Request& req) {
    return req.print(out);
}

/******************************************************************************/
// Request Completion State

void Request::wait(bool measure_time) {

    Stats::ScopedWaitTimer wait_timer(
        _type == READ ? Stats::WaitOp::WOP_READ : Stats::WaitOp::WOP_WRITE,
        measure_time);

    _state.waitFor(READY2DIE);

    check_error();
}

bool Request::cancel() {
    if (!_file) return false;
	RequestPtr req = shared_from_this();
    if (DiskQueues::getInstance()->CancelRequest(req, _file->get_queue_id()))
    {
        _state.setTo(DONE);
        // user callback
        if (_on_complete)
            _on_complete(this, false);
        _file.reset();
        _state.setTo(READY2DIE);
        return true;
    }
    return false;
}

bool Request::poll() {
    const State s = _state.get();

    check_error();

    return s == DONE || s == READY2DIE;
}

void Request::completed(int err, bool canceled) {
	_saved_error = err;
		// change state
    _state.setTo(DONE);
    // user callback
    if (_on_complete)
        _on_complete(this, !canceled);
    // notify waiters
    _file.reset();
    _state.setTo(READY2DIE);
}

}
